#ifndef RAYTRACINGTOOLCUH
#define RAYTRACINGTOOLCUH
#include"ray.cuh"
#include"curand_kernel.h"
#define __duel__ __host__ __device__
#define checkCudaErrors(val) check_cuda( (val), #val, __FILE__, __LINE__ )

void check_cuda(cudaError_t result, char const *const func, const char *const file, int const line) {
    if (result) {
        std::cerr << "CUDA error = " << static_cast<unsigned int>(result) << " at " <<
            file << ":" << line << " '" << func << "' \n";
        // Make sure we call CUDA Device Reset before exiting
        cudaDeviceReset();
        exit(99);
    }
}
#define THISNULL(ptr)\
    if(ptr)\
    {delete ptr;ptr=nullptr;}
__duel__ inline float ffmin(float a, float b) { return a <= b ? a : b; }
__duel__ inline float ffmax(float a, float b) { return a >= b ? a : b; }
// namespace tools
// {
// template<class T>
// __device__ inline void swap(T&a,T&b)
// {
//     T c=a;
//     a=b;
//     b=c;
// }
// }
/**
 * @brief 线性插值
 * @param t 0~1
 * @param a 起始值、
 * @param b 终止值
 * @return 返回a+t*(b-a)
*/
__device__ inline float lerp(float t,float a,float b)
{
    return a+t*(b-a);
}
__device__ float schlick(float cosine, float ref_idx) {
    float r0 = (1.0f-ref_idx) / (1.0f+ref_idx);
    r0 = r0*r0;
    return r0 + (1.0f-r0)*pow((1.0f - cosine),5.0f);
};
__device__ float clamp(float value,float min,float max)
{
    if(value<=min)return min;
    if(value>=max)return max;
    return value;
}
__device__ void get_sphere_uv(const vec3& p, float& u, float& v) {
    auto phi = atan2(p.z(), p.x());
    auto theta = asin(p.y());
    u = 1-(phi + M_PI) / (2*M_PI);
    v = (theta + M_PI/2) / M_PI;
}
//折射
__device__ vec3 refract(const vec3& uv, const vec3& n, float ni_over_nt) {    
    float cos_theta = dot(-uv, n);
    vec3 r_out_parallel =  ni_over_nt * (uv + cos_theta*n);
    vec3 r_out_perp = -sqrt(1.0 - r_out_parallel.squared_length()) * n;
    return r_out_parallel + r_out_perp;
}

#define RANDVEC3 vec3(curand_uniform(local_rand_state),curand_uniform(local_rand_state),curand_uniform(local_rand_state))
__device__ inline unsigned int rand_int(unsigned int start,unsigned int end,curandState *local_rand_state)
{
    return curand(local_rand_state)%(end-start+1)+start;
}
__device__ vec3 random_in_unit_sphere(curandState *local_rand_state) {
    vec3 p;
    do {
        p = 2.0f*RANDVEC3 - vec3(1,1,1);
    } while (p.squared_length() >= 1.0f);
    return p;
}
__device__ vec3 random_cosine_direction(curandState *local_rand_state) {
    float r1=curand_uniform(local_rand_state);
    float r2=curand_uniform(local_rand_state);
    float z=sqrt(1-r2);
    float phi=2*M_PI*r1;
    float sqrt_r2=sqrt(r2);
    float x=cos(phi)*sqrt_r2;
    float y=sin(phi)*sqrt_r2;
    return vec3(x,y,z);

}
//反射
__device__ vec3 reflect(const vec3& v, const vec3& n) {
     return v - 2.0f*dot(v,n)*n;
}
class onb
{
public:
    __duel__ onb() {}
    __duel__ inline vec3 operator[](int i) const { return axis[i]; }
    __duel__ vec3 u() const       { return axis[0]; }
    __duel__ vec3 v() const       { return axis[1]; }
    __duel__ vec3 w() const       { return axis[2]; }
    __duel__ vec3 local(float a, float b, float c) const { return a*u() + b*v() + c*w(); }
    __duel__ vec3 local(const vec3& a) const { return a.x()*u() + a.y()*v() + a.z()*w(); }
    __duel__ void build_from_w(const vec3&);
    vec3 axis[3];
};
__duel__ void onb::build_from_w(const vec3&n)
{
    axis[2] = unit_vector(n);
    vec3 a;
    if (fabs(w().x()) > 0.9)
        a = vec3(0, 1, 0);
    else
        a = vec3(1, 0, 0);
    axis[1] = unit_vector(cross(w(), a));
    axis[0] = cross(w(), v());

}


#endif